﻿/*
 *  This file is part of SXAPI.
 *
 *  SXAPI is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, version 3 of the License.

 *  SXAPI is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with SXAPI.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace StackExchangeApi
{
    /// <summary>
    /// Represents a Badge that exists on all Trilogy Sites.
    /// 
    /// Provides methods for looking up the specific Badge
    /// for each Trilogy Site.
    /// 
    /// If the Trilogy Site does not have the particular badge,
    /// null is provided.
    /// 
    /// For example, the Beta badge does not exist on Meta.StackOverflow.
    /// </summary>
    public sealed class GeneralBadge
    {
        private Dictionary<StackExchangeSite, Badge> m_badges = new Dictionary<StackExchangeSite, Badge>();

        /// <summary>
        /// Find the specific Badge of this GeneralBadge
        /// given the site it resides on.
        /// </summary>
        /// <param name="site">The site in question</param>
        /// <returns>The specific Badge version of this GeneralBadge</returns>
        public Badge this[StackExchangeSite site]
        {
            get
            {
                Badge ret;
                m_badges.TryGetValue(site, out ret);

                return ret;
            }

            internal set
            {
                m_badges[site] = value;
            }
        }

        /// <summary>
        /// No publicly visible constructor
        /// </summary>
        internal GeneralBadge() { }

        /// <summary>
        /// Consutruct a new GeneralBadge
        /// </summary>
        /// <param name="ids">Map of sites to the ids of the Badges represented by the GeneralBadge</param>
        /// <param name="name">Universal name of the GeneralBadge</param>
        /// <param name="description">Description of the GeneralBadge</param>
        /// <param name="c">Universal class of the GeneralBadge</param>
        internal GeneralBadge(Dictionary<StackExchangeSite, long> ids, String name, String description, BadgeClass c)
        {
            foreach (StackExchangeSite site in ids.Keys)
            {
                this[site] = new Badge(ids[site], name, description, site, c, BadgeType.General);
            }
        }

        /// <summary>
        /// Construct a new GeneralBadge
        /// </summary>
        /// <param name="id">Common id for the Badge on all the "Trilogy Sites"</param>
        /// <param name="name">Name of the Badge</param>
        /// <param name="description">Description of the Badge</param>
        /// <param name="c">Class of the Badge</param>
        internal GeneralBadge(long id, String name, String description, BadgeClass c)
        {
            foreach (StackExchangeSite site in Enum.GetValues(typeof(StackExchangeSite)))
            {
                if (site == StackExchangeSite.ServerFault || site == StackExchangeSite.StackOverflow || id < 35)
                    this[site] = new Badge(id, name, description, site, c, BadgeType.General);
                else
                    this[site] = new Badge(id - 2, name, description, site, c, BadgeType.General);
            }
        }
    }

    /// <summary>
    /// Container class for all the known
    /// general badges.
    /// </summary>
    public sealed class GeneralBadges
    {
        /// <summary>
        /// Autobiographer Badge
        /// </summary>
        public static readonly GeneralBadge Autobiographer = new GeneralBadge(9, "Autobiographer", "Completed all user profile fields.", BadgeClass.Bronze);

        /// <summary>
        /// Beta Badge
        /// </summary>
        public static readonly GeneralBadge Beta = new GeneralBadge(30, "Beta", "Actively participated in the private beta.", BadgeClass.Silver);

        /// <summary>
        /// Citizen Patrol Badge
        /// </summary>
        public static readonly GeneralBadge CitizenPatrol = new GeneralBadge(8, "Citizen Patrol", "First flagged post.", BadgeClass.Bronze);

        /// <summary>
        /// Civic Duty Badge
        /// </summary>
        public static readonly GeneralBadge CivicDuty = new GeneralBadge(32, "Civic Duty", "Voted 300 times.", BadgeClass.Silver);

        /// <summary>
        /// Cleanup Badge
        /// </summary>
        public static readonly GeneralBadge Cleanup = new GeneralBadge(4, "Cleanup", "First rollback.", BadgeClass.Bronze);

        /// <summary>
        /// Commentator Badge
        /// </summary>
        public static readonly GeneralBadge Commentator = new GeneralBadge(31, "Commentator", "Left 10 comments.", BadgeClass.Bronze);

        /// <summary>
        /// Critic Badge
        /// </summary>
        public static readonly GeneralBadge Critic = new GeneralBadge(7, "Critic", "First down vote.", BadgeClass.Bronze);

        /// <summary>
        /// Disciplined Badge
        /// </summary>
        public static readonly GeneralBadge Disciplined = new GeneralBadge(37, "Disciplined", "Deleted own post with score of 3 or higher.", BadgeClass.Bronze);

        /// <summary>
        /// Editor Badge
        /// </summary>
        public static readonly GeneralBadge Editor = new GeneralBadge(3, "Editor", "First edit.", BadgeClass.Bronze);

        /// <summary>
        /// Electorate Badge
        /// </summary>
        public static readonly GeneralBadge Electorate = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 54 },    
                { StackExchangeSite.ServerFault, 75 },
                { StackExchangeSite.StackOverflow, 155 },
                { StackExchangeSite.SuperUser, 49 }
            },
            "Electorate",
            "Voted on 600 questions and has better than 3:1 answer/question vote ratio",
            BadgeClass.Gold);

        /// <summary>
        /// Enlightened Badge
        /// </summary>
        public static readonly GeneralBadge Enlightened = new GeneralBadge(19, "Enlightened", "First answer was accepted with at least 10 up votes.", BadgeClass.Silver);

        /// <summary>
        /// Enthusiast Badge
        /// </summary>
        public static readonly GeneralBadge Enthusiast = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 39 },
                { StackExchangeSite.ServerFault, 66 },
                { StackExchangeSite.StackOverflow, 71 },
                { StackExchangeSite.SuperUser, 39 }
            },
            "Enthusiast",
            "Visited the site each day for 30 consecutive days.",
            BadgeClass.Silver);

        /// <summary>
        /// Epic Badge
        /// </summary>
        public static readonly GeneralBadge Epic = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 51 },
                { StackExchangeSite.ServerFault, 73 },
                { StackExchangeSite.StackOverflow, 145 },
                { StackExchangeSite.SuperUser, 44 }
            },
            "Epic",
            "Hit the daily reputation cap on 50 days.",
            BadgeClass.Silver);

        /// <summary>
        /// FamousQuestion Badge
        /// </summary>
        public static readonly GeneralBadge FamousQuestion = new GeneralBadge(28, "Famous Question", "Asked a question with 10,000 views.", BadgeClass.Gold);

        /// <summary>
        /// Fanatic Badge
        /// </summary>
        public static readonly GeneralBadge Fanatic = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 53 },
                { StackExchangeSite.ServerFault, 67 },
                { StackExchangeSite.StackOverflow, 83 },
                { StackExchangeSite.SuperUser, 42 }
            },
            "Fanatic",
            "Visited the site each day for 100 consecutive days.",
            BadgeClass.Gold);

        /// <summary>
        /// Favorite Question Badge
        /// </summary>
        public static readonly GeneralBadge FavoriteQuestion = new GeneralBadge(33, "Favorite Question", "Question favorited by 25 users.", BadgeClass.Silver);

        /// <summary>
        /// Generalist Badge
        /// </summary>
        public static readonly GeneralBadge Generalist = new GeneralBadge(15, "Generalist", "Active in many different tags.", BadgeClass.Silver);

        /// <summary>
        /// Good Answer Badge
        /// </summary>
        public static readonly GeneralBadge GoodAnswer = new GeneralBadge(24, "Good Answer", "Answer voted up 25 times.", BadgeClass.Silver);

        /// <summary>
        /// Good Question Badge
        /// </summary>
        public static readonly GeneralBadge GoodQuestion = new GeneralBadge(21, "Good Question", "Question voted up 25 times.", BadgeClass.Silver);

        /// <summary>
        /// Great Answer Badge
        /// </summary>
        public static readonly GeneralBadge GreatAnswer = new GeneralBadge(25, "Great Answer", "Answer voted up 100 times.", BadgeClass.Gold);

        /// <summary>
        /// Great Question Badge
        /// </summary>
        public static readonly GeneralBadge GreatQuestion = new GeneralBadge(22, "Great Question", "Question voted up 100 times.", BadgeClass.Gold);

        /// <summary>
        /// Guru Badge
        /// </summary>
        public static readonly GeneralBadge Guru = new GeneralBadge(18, "Guru", "Accepted answer and voted up 40 times.", BadgeClass.Silver);

        /// <summary>
        /// Legendary Badge
        /// </summary>
        public static readonly GeneralBadge Legendary = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 52 },
                { StackExchangeSite.ServerFault, 74 },
                { StackExchangeSite.StackOverflow, 146 },
                { StackExchangeSite.SuperUser, 45 }
            },
            "Legendary",
            "Hit the daily reputation cap on 150 days.",
            BadgeClass.Gold);

        /// <summary>
        /// Mortarboard Badge
        /// </summary>
        public static readonly GeneralBadge Mortarboard = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 50 },
                { StackExchangeSite.ServerFault, 72 },
                { StackExchangeSite.StackOverflow, 144 },
                { StackExchangeSite.SuperUser, 43 }
            },
            "Mortarboard",
            "Hit the daily reputation cap for the first time.",
            BadgeClass.Bronze);

        /// <summary>
        /// Necromancer Badge
        /// </summary>
        public static readonly GeneralBadge Necromancer = new GeneralBadge(17, "Necromancer", "Answered a question more than 60 days later with at least 5 votes.", BadgeClass.Silver);

        /// <summary>
        /// Nice Answer Badge
        /// </summary>
        public static readonly GeneralBadge NiceAnswer = new GeneralBadge(23, "Nice Answer", "Answer voted up 10 times.", BadgeClass.Bronze);

        /// <summary>
        /// Nice Question Badge
        /// </summary>
        public static readonly GeneralBadge NiceQuestion = new GeneralBadge(20, "Nice Question", "Question voted up 10 times.", BadgeClass.Bronze);

        /// <summary>
        /// Notable Question Badge
        /// </summary>
        public static readonly GeneralBadge NotableQuestion = new GeneralBadge(27, "Notable Question", "Asked a question with 2,500 views.", BadgeClass.Silver);

        /// <summary>
        /// Organizer Badge
        /// </summary>
        public static readonly GeneralBadge Organizer = new GeneralBadge(5, "Organizer", "First retag.", BadgeClass.Bronze);

        /// <summary>
        /// Peer Pressure Badge
        /// </summary>
        public static readonly GeneralBadge PeerPressure = new GeneralBadge(38, "Peer Pressure", "Deleted own post with score of -3 or lower.", BadgeClass.Bronze);

        /// <summary>
        /// Popular Question Badge
        /// </summary>
        public static readonly GeneralBadge PopularQuestion = new GeneralBadge(26, "Popular Question", "Asked a question with 1,000 views.", BadgeClass.Bronze);

        /// <summary>
        /// Populist Badge
        /// </summary>
        public static readonly GeneralBadge Populist = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 37 },
                { StackExchangeSite.ServerFault, 62 },
                { StackExchangeSite.StackOverflow, 62 },
                { StackExchangeSite.SuperUser, 37 }
            },
            "Populist",
            "Provided an answer that outscored an accepted answer with 10 votes by 2x.",
            BadgeClass.Gold);

        /// <summary>
        /// Pundit Badge
        /// </summary>
        public static readonly GeneralBadge Pundit = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 40 },
                { StackExchangeSite.ServerFault, 68 },
                { StackExchangeSite.StackOverflow, 94 },
                { StackExchangeSite.SuperUser, 40 }
            },
            "Pundit",
            "Left 10 comments with score of 10 or more.",
            BadgeClass.Silver);

        /// <summary>
        /// Reversal Badge
        /// </summary>
        public static readonly GeneralBadge Reversal = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 41 },
                { StackExchangeSite.ServerFault, 69 },
                { StackExchangeSite.StackOverflow, 95 },
                { StackExchangeSite.SuperUser, 47 }
            },
            "Reversal",
            "Provided answer of +20 score to a question of -5 score.",
            BadgeClass.Gold);

        /// <summary>
        /// Scholar Badge
        /// </summary>
        public static readonly GeneralBadge Scholar = new GeneralBadge(10, "Scholar", "First accepted answer on your own question.", BadgeClass.Bronze);

        /// <summary>
        /// Self Learner Badge
        /// </summary>
        public static readonly GeneralBadge SelfLearner = new GeneralBadge(14, "Self-Learner", "Answered your own question with at least 3 up votes.", BadgeClass.Bronze);

        /// <summary>
        /// Stellar Question Badge
        /// </summary>
        public static readonly GeneralBadge StellarQuestion = new GeneralBadge(36, "Stellar Question", "Question favorited by 100 users.", BadgeClass.Gold);

        /// <summary>
        /// Strunk &amp; White Badge
        /// </summary>
        public static readonly GeneralBadge StrunkWhite = new GeneralBadge(12, "Strunk & White", "Edited 100 entries.", BadgeClass.Silver);

        /// <summary>
        /// Student Badge
        /// </summary>
        public static readonly GeneralBadge Student = new GeneralBadge(2, "Student", "Asked first question with at least one up vote.", BadgeClass.Bronze);

        /// <summary>
        /// Supporter Badge
        /// </summary>
        public static readonly GeneralBadge Supporter = new GeneralBadge(6, "Supporter", "First up vote.", BadgeClass.Bronze);

        /// <summary>
        /// Taxonomist Badge
        /// </summary>
        public static readonly GeneralBadge Taxonomist = new GeneralBadge(11, "Taxonomist", "Created a tag used by 50 questions.", BadgeClass.Silver);

        /// <summary>
        /// Teacher Badge
        /// </summary>
        public static readonly GeneralBadge Teacher = new GeneralBadge(1, "Teacher", "Answered first question with at least one up vote.", BadgeClass.Bronze);

        /// <summary>
        /// Tumbleweed Badge
        /// </summary>
        public static readonly GeneralBadge Tumbleweed = new GeneralBadge(
            new Dictionary<StackExchangeSite, long>()
            {
                { StackExchangeSite.Meta, 38 },
                { StackExchangeSite.ServerFault, 63 },
                { StackExchangeSite.StackOverflow, 63 },
                { StackExchangeSite.SuperUser, 38 }
            },
            "Tumbleweed",
            "Asked a question with no answers, no comments, and low views for a week.",
            BadgeClass.Bronze);

        /// <summary>
        /// Yearling Badge
        /// </summary>
        public static readonly GeneralBadge Yearling = new GeneralBadge(13, "Yearling", "Active member for a year, earning at least 200 reputation.", BadgeClass.Silver);

        internal static Dictionary<Pair<StackExchangeSite, long>, Badge> LOOKUP_TABLE = new Dictionary<Pair<StackExchangeSite, long>, Badge>();

        /// <summary>
        /// Populate LOOKUP_TABLE with all the known badges at runtime.
        /// </summary>
        static GeneralBadges()
        {
            FieldInfo[] fields = typeof(GeneralBadges).GetFields();

            foreach (FieldInfo field in fields)
            {
                GeneralBadge badge = (GeneralBadge)field.GetValue(null);

                foreach (StackExchangeSite site in Enum.GetValues(typeof(StackExchangeSite)))
                    LOOKUP_TABLE[new Pair<StackExchangeSite, long>(site, badge[site].Id)] = badge[site];
            }

            //Meta has no Beta badge
            Beta[StackExchangeSite.Meta] = null;
        }

        /// <summary>
        /// Hiding public constructor
        /// </summary>
        internal GeneralBadges() { }

        /// <summary>
        /// Find a Badge by site &amp; id, if it exists
        /// </summary>
        /// <param name="site">The site the Badge is on</param>
        /// <param name="id">Id of the Badge we're looking for</param>
        /// <returns>The Badge, if found, or null</returns>
        internal static Badge GetBadgeBySiteAndId(StackExchangeSite site, long id)
        {
            Badge ret;
            LOOKUP_TABLE.TryGetValue(new Pair<StackExchangeSite, long>(site, id), out ret);

            return ret;
        }
    }
}